﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace LambdaExtensions
{
    public static class EnumerableExtensions
    {

        /// <summary>
        /// synchronizes an enumerable to a list, 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <param name="selector"></param>
        /// <param name="creator"></param>
        public static void SyncList<TSource, TDestination>(IEnumerable<TSource> source, IList destination, Func<TDestination, TSource> selector, Func<TSource, TDestination> creator)
            where TDestination : class
            where TSource : class
        {
            if (source == null)
                throw new ArgumentNullException("source");

            if (destination == null)
                throw new ArgumentNullException("destination");

            if (selector == null)
                throw new ArgumentNullException("selector");

            if (creator == null)
                throw new ArgumentNullException("creator");

            var syncObject = destination.IsSynchronized ? destination.SyncRoot : new object();

            lock (syncObject)
            {
                var ExistingItems = destination.OfType<TDestination>().Where(d => selector(d) != null).ToDictionary(d => selector(d));

                foreach (var s in source)
                {
                    if (!ExistingItems.Remove(s))
                    {
                        var NewObject = creator(s);
                        if (selector(NewObject) != s)
                            throw new ArgumentException("creator and selector must return same value");

                        destination.Add(creator(s));
                    }
                }

                var RemovedItems = ExistingItems.Values;
                foreach (var i in RemovedItems)
                {
                    destination.Remove(i);
                }

            }

        }
    }
}
